package gameManager;

import java.io.File;
import java.io.IOException;
import java.util.*;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import gui.*;
import zarzadcyZdarzen.*;
import bierki.*;
/**
 * Logika gry, warstwa pod widoczną szachownicą
 * @author Konrad Sobczak 
 * @author Kamil Nowak
 *
 */ 
public class GameManager {
   
	private Vector<Bierka> mojeBierki; //bezpieczne watkowo, z ArrayList trzeba kombinowac
	private Vector<Bierka> wrazeBierki;
	private Vector<Bierka> mojeUtraconeBierki;
	private ChessTableWindow chessGuiWindow;
	private volatile Bierka[][] szachownica;
	private TableManager myManager;
	private EventDispacher ed;
	private int id_stolu;
	private boolean tura; 
	private boolean czy_czarny;
	private Krol mojKrol;
	private Krol wrogKrol;
	private BierkaKolor mojKolor;
	
	/**
	 * 
	 * @param manTable menadzer wszystkich stolow
	 * @param ed EventDispatcher, on odpowiada za wyslanie zdarzen generowanych przez gracza
	 * @param id_stolu trzeba wiedziec na jakim stole sie gra, zeby moc informowac serwer o swoich poczynaniach
	 * @param czy_czarny no i jakim kolorem bierek sie gra
	 */
	public GameManager(TableManager manTable, EventDispacher ed, int id_stolu,boolean czy_czarny){
		
		this.czy_czarny = czy_czarny;
		if(this.czy_czarny)
			mojKolor=BierkaKolor.CZARNY;
		else
			mojKolor=BierkaKolor.BIALY;
		
		this.id_stolu = id_stolu;
		this.szachownica = new Bierka[8][8];
		this.mojeUtraconeBierki = new Vector<Bierka>();
		Vector<Bierka>bialeBierki = new Vector<Bierka>();
		Vector<Bierka>czarneBierki = new Vector<Bierka>();
		
		
		
		for(int i=0;i<szachownica.length;i++){
			for(int j=0;j<szachownica[1].length;j++){
				if(j==1){
					czarneBierki.add(szachownica[i][j]=new Pionek("gfx/pionek.png",BierkaKolor.CZARNY,i,j)); //czarny pionek
				}
				else if(j==6){
					bialeBierki.add(szachownica[i][j]=new Pionek("gfx/pionekbialy.png",BierkaKolor.BIALY,i,j));
				}
				else{
					szachownica[i][j]=null;
				}
			}
		}
		 
		czarneBierki.add(szachownica[0][0]=new Wieza("gfx/wieza.png",BierkaKolor.CZARNY,0,0));
		czarneBierki.add(szachownica[7][0]=new Wieza("gfx/wieza.png",BierkaKolor.CZARNY,7,0));
		czarneBierki.add(szachownica[1][0]=new Konik("gfx/kon.png",BierkaKolor.CZARNY,1,0));
		czarneBierki.add(szachownica[6][0]=new Konik("gfx/kon.png",BierkaKolor.CZARNY,6,0));
		czarneBierki.add(szachownica[3][0]=new Krolowa("gfx/krolowa.png",BierkaKolor.CZARNY,3,0));
		czarneBierki.add(szachownica[2][0]=new Goniec("gfx/goniec.png",BierkaKolor.CZARNY,2,0));
		czarneBierki.add(szachownica[5][0]=new Goniec("gfx/goniec.png",BierkaKolor.CZARNY,5,0));
		
		bialeBierki.add(szachownica[0][7]=new Wieza("gfx/wiezabiala.png",BierkaKolor.BIALY,0,7));
		bialeBierki.add(szachownica[7][7]=new Wieza("gfx/wiezabiala.png",BierkaKolor.BIALY,7,7));
		bialeBierki.add(szachownica[1][7]=new Konik("gfx/konbialy.png",BierkaKolor.BIALY,1,7));
		bialeBierki.add(szachownica[6][7]=new Konik("gfx/konbialy.png",BierkaKolor.BIALY,6,7));
		bialeBierki.add(szachownica[3][7]=new Krolowa("gfx/krolowabiala.png",BierkaKolor.BIALY,3,7));
		bialeBierki.add(szachownica[2][7]=new Goniec("gfx/goniecbialy.png",BierkaKolor.BIALY,2,7));
		bialeBierki.add(szachownica[5][7]=new Goniec("gfx/goniecbialy.png",BierkaKolor.BIALY,5,7));
		
		if(this.czy_czarny){
			czarneBierki.add(szachownica[4][0]=this.mojKrol=new Krol("gfx/krol.png",BierkaKolor.CZARNY,4,0));
			bialeBierki.add(szachownica[4][7]=this.wrogKrol=new Krol("gfx/krolbialy.png",BierkaKolor.BIALY,4,7));
			this.mojeBierki=czarneBierki;
			this.wrazeBierki=bialeBierki;
		}
		else{
			czarneBierki.add(szachownica[4][0]=this.wrogKrol=new Krol("gfx/krol.png",BierkaKolor.CZARNY,4,0));
			bialeBierki.add(szachownica[4][7]=this.mojKrol=new Krol("gfx/krolbialy.png",BierkaKolor.BIALY,4,7));
			this.mojeBierki=bialeBierki;
			this.wrazeBierki=czarneBierki;
		}
		
		this.tura=false;
		this.myManager = manTable;
		this.ed = ed;
		this.chessGuiWindow = new ChessTableWindow(this,czy_czarny);
		
	}
	
	/**
	 * 
	 * @return zwraca wartość logiczną informującą pośrednio o tym jakim kolorem bierek gracz dysponuje w partii
	 */
	public  boolean get_czy_czarny(){
		return this.czy_czarny;
	}
	
	/**
	 * Zmienia ture, po naszej nastepuje tura przeciwnika w ktorej nie mozemy ruszac bierek
	 *
	 */
	public void tura(){
		this.tura=!this.tura;
	}
	
	/**
	 * Przydatna bywa wiedza o tym czy jest teraz nasza tura
	 * @return zwraca prawdę jeśli jest nasz ruch
	 */
	public boolean get_tura(){
		return this.tura;
	}
	
	/**
	 * Szachownice musi być dostępna, np do narysowania w oknie gry
	 * @return zwraca aktualną szachownice jako tablice [][] Bierek.
	 */
	public Bierka[][] daj_szachownice(){
		return this.szachownica;
	}
	
	/*public synchronized void aktualizuj_szachownice(int i, int j, int ikoncowe, int jkoncowe){
		
		if(i!=ikoncowe || j!=jkoncowe){
			if(validate_move(i,j,ikoncowe,jkoncowe)==true){
				if(szachownica[ikoncowe][jkoncowe]!=null){
					if(szachownica[ikoncowe][jkoncowe].getKolor()==this.mojKolor){
						if(!(szachownica[ikoncowe][jkoncowe] instanceof Pionek))
							mojeUtraconeBierki.add(szachownica[ikoncowe][jkoncowe]);
						mojeBierki.remove(szachownica[ikoncowe][jkoncowe]);
					}
					else{
						wrazeBierki.remove(szachownica[ikoncowe][jkoncowe]);
					}
				}
				szachownica[i][j].move(ikoncowe,jkoncowe);
				szachownica[ikoncowe][jkoncowe]=szachownica[i][j];
				szachownica[i][j]=null;
				
				if(szachownica[ikoncowe][jkoncowe] instanceof Pionek && szachownica[ikoncowe][jkoncowe].getKolor()==mojKolor){
					switch(mojKolor){
						case BIALY:
							if(jkoncowe==0){
								Bierka prom;
								if((prom = chessGuiWindow.wypromujOkno(mojeUtraconeBierki))!=null){
									prom.move(ikoncowe,jkoncowe);
									szachownica[ikoncowe][jkoncowe]=prom;
								}
							}
							break;
						case CZARNY:
							if(jkoncowe==7){
								Bierka prom;
								if((prom = chessGuiWindow.wypromujOkno(mojeUtraconeBierki))!=null){
									prom.move(ikoncowe,jkoncowe);
									szachownica[ikoncowe][jkoncowe]=prom;
								}
							}
							break;
					}
				}
				
				
				if(get_tura()){
					ed.sendMove(id_stolu,i,j,ikoncowe,jkoncowe);
					System.out.println("sie tu znalazlem");
				}
				else{
					ed.sendMoveOk(id_stolu);
				}
				
				this.tura();
				chessGuiWindow.repaint();
			}
		}
	}
	
	public boolean validate_move(int i, int j, int ikoncowe, int jkoncowe){
		if(szachownica[i][j].getKolor()==mojKolor && szachownica[i][j]!=mojKrol)
		return (szachownica[i][j].sprawdzRuch(szachownica,ikoncowe,jkoncowe)
				&& !szachownica[i][j].sprawdzSzach(szachownica, ikoncowe, jkoncowe, mojKrol));
		else
			return szachownica[i][j].sprawdzRuch(szachownica,ikoncowe,jkoncowe);
	}
	*/
	
	
	
	/**
	 * Najtrudniejsza do napisania metoda w całej grze, sprawdza prawidłowość ruchu, bicia, promocje pionków etc.
	 * @param i - pozycja początkowa x
	 * @param j - pozycja początkowa y
	 * @param ikoncowe - pozycja koncowa x
	 * @param jkoncowe - pozycja koncowa y	 
	 */
	public synchronized void aktualizuj_szachownice(int i, int j, int ikoncowe, int jkoncowe){
		
		if(i!=ikoncowe || j!=jkoncowe){
			if(get_tura()){
				if(validate_move(i,j,ikoncowe,jkoncowe,mojKrol)==true){
					
					if(szachownica[ikoncowe][jkoncowe]!=null){
						wrazeBierki.remove(szachownica[ikoncowe][jkoncowe]);
					}
					
					szachownica[i][j].move(ikoncowe,jkoncowe);
					szachownica[ikoncowe][jkoncowe]=szachownica[i][j];
					
					if(szachownica[i][j] instanceof Pionek && szachownica[i][j].getKolor()==mojKolor){
						switch(mojKolor){
							case BIALY:
								if(jkoncowe==0){
									Bierka prom;
									if((prom = chessGuiWindow.wypromujOkno(mojeUtraconeBierki))!=null){
										prom.move(ikoncowe,jkoncowe);
										szachownica[ikoncowe][jkoncowe]=prom;
										if(prom instanceof Goniec)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,1);
										if(prom instanceof Konik)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,2);
										if(prom instanceof Wieza)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,3);
										if(prom instanceof Krolowa)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,4);
										mojeBierki.add(prom);
									}
									else
										ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,0);
								}
								else
									ed.sendMove(id_stolu,i,j,ikoncowe,jkoncowe);
								break;
							case CZARNY:
								if(jkoncowe==7){
									Bierka prom;
									if((prom = chessGuiWindow.wypromujOkno(mojeUtraconeBierki))!=null){
										prom.move(ikoncowe,jkoncowe);
										szachownica[ikoncowe][jkoncowe]=prom;
										if(prom instanceof Goniec)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,-1);
										if(prom instanceof Konik)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,-2);	
										if(prom instanceof Wieza)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,-3);
										if(prom instanceof Krolowa)
											ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,-4);
										mojeBierki.add(prom);
									}
									else
										ed.sendPromo(id_stolu,i,j,ikoncowe,jkoncowe,0);
								}
								else
									ed.sendMove(id_stolu,i,j,ikoncowe,jkoncowe);
								break;
						}
					}
					else
						ed.sendMove(id_stolu,i,j,ikoncowe,jkoncowe);
					
					/*if(szachownica[i][j].sprawdzSzach(szachownica,ikoncowe,jkoncowe,wrogKrol)){
						if(wrogKrol.mat(szachownica,mojeBierki))
							System.out.println("MAT!!!");
						//tu wypisywac ze szachuje
					}*/
					
					szachownica[i][j]=null;
					
					if(wrogKrol.szach(szachownica)){
						if(wrogKrol.mat(szachownica,wrazeBierki)){
							//System.out.println("MAT!!!");
							ed.sendFinishGame(id_stolu);
							chessGuiWindow.szach("Dales przeciwnikowi mata, wygrales!!!");
							
						}
						else{
							chessGuiWindow.szach("Trzymasz przeciwnika w szachu!!!");
						}
						//tu wypisywac ze szachuje
					}
					
					this.tura();
				} 
			}
			else{
				if(validate_move(i,j,ikoncowe,jkoncowe,mojKrol)==true || szachownica[i][j].sprawdzSzach(szachownica,ikoncowe,jkoncowe,mojKrol)==true){
					
					if(szachownica[ikoncowe][jkoncowe]!=null){
						if(szachownica[ikoncowe][jkoncowe].getKolor()==this.mojKolor){
							if(!(szachownica[ikoncowe][jkoncowe] instanceof Pionek))
								mojeUtraconeBierki.add(szachownica[ikoncowe][jkoncowe]);
							mojeBierki.remove(szachownica[ikoncowe][jkoncowe]);
						}
						
					}
					
					szachownica[i][j].move(ikoncowe,jkoncowe);
					szachownica[ikoncowe][jkoncowe]=szachownica[i][j];
					szachownica[i][j]=null;
					
					if(szachownica[ikoncowe][jkoncowe].getTyp()==BierkaTyp.PIONEK && (jkoncowe==0 || jkoncowe==7))
						 ed.SendPromoOk(id_stolu);
					else
						ed.sendMoveOk(id_stolu);
					
					
					//if(szachownica[i][j].sprawdzSzach(szachownica,ikoncowe,jkoncowe,mojKrol)==true){
					if (mojKrol.szach(szachownica)) {
						if(mojKrol.mat(szachownica,mojeBierki)){
							chessGuiWindow.szach("Dostales mata, przegrales!!!");
							ed.sendFinishOk(id_stolu);
						}
						else
							chessGuiWindow.szach("Jestes szachowany, bron sie!!!");
					}
					 
					
					this.tura();
				}
				Thread t = new Thread(new OdgrywaczDzwieku());
				t.start();
			}
			chessGuiWindow.repaint();
		
		}
	}
	
	/**
	 * Sprawdza poprawnosc ruchu, król potrzebny jest aby sprawdzić czy nie 
	 * wystawiamy go na bicie (szach)
	 * @param i pozatkowa pozycja x Bierki
	 * @param j poczatkowa pozycja y Bierki
	 * @param ikoncowe koncowa pozycja x Bierki
	 * @param jkoncowe koncowa pozycja y Bierki
	 * @return zwraca fałsz jeśli ruch jest niedopuszczalny, lub prawdę jeśli zgodny z zasadami gry
	 */
	public boolean validate_move(int i, int j, int ikoncowe, int jkoncowe){
		return validate_move(i,j,ikoncowe,jkoncowe,mojKrol);
	}
	
	/**
	 * Sprawdzenie poprawności ruchu bierki, dodatkowo sprawdza czy nie szachujemy króla przeciwnika
	 * @param i pozatkowa pozycja x Bierki
	 * @param j poczatkowa pozycja y Bierki
	 * @param ikoncowe koncowa pozycja x Bierki
	 * @param jkoncowe koncowa pozycja y Bierki
	 * @param krol potrzebny jest bowiem do sprawdzania zachodzenia sytuacji "szach" i "mat"
	 * @return zwraca prawdę gdy ruch prawidłowy, fałsz gdy nie
	 */
	public boolean validate_move(int i, int j, int ikoncowe, int jkoncowe, Krol krol){
		return szachownica[i][j].sprawdzRuch(szachownica,ikoncowe,jkoncowe,krol);
	}

	/**
	 * Dorzuca wypowiedź przeciwnika do okienka czatu
	 * @param kto Nickname gracza
	 * @param co tresc wypowiedzi
	 */
	public void dodajChatMsg(String kto, String co){
		chessGuiWindow.dodajChatMsg(kto,co);
	}
	
	/**
	 * Wysyła naszą wiadomość czat
	 * @param co treść wiadomości
	 */
	public  void sendChatMsg(String co){
		ed.sendChatMsg(id_stolu,co);
	}
	
	/** rozpoczyna nową grę
	 * 
	 * @param imie imię gracza wchodzącego do gry
	 */
	public void rozpocznij_gre(String imie){
		chessGuiWindow.rozpocznij_gre(imie);
		this.tura();
	}
	
	public void zamknij_z_zewnatrz(){
		chessGuiWindow.zamknij_z_zewnatrz();
	}
	
	/** 
	 * Awaryjne zamknięcie stołu - np. z powodu wyjścia przeciwnika z gry
	 * @param imie imię tego kto opuscił grę.
	 */
	public void koniec_gry(String imie){
		chessGuiWindow.koniec_gry(imie);
	}
	
	/**
	 * Zamyka nasz stół poprzez poinformowanie swojego manadzera ze jest juz niepotrzebny
	 *
	 */
	public void zamknij_stol(){
		myManager.zamknij_stol(id_stolu);
	}

	/**
	 * Jesli przeciwnik promował swojego pionka na wyższą figurę, metoda wywoływana jest po stronie drugiego żeby uaktualnić jego szachownice.
	 * @param xk  konocwe polozenie Bierki w szachownicy
	 * @param yk  koncowe polozenoe wg. wspołrzędnych osi Y, równych w tym przypadku 140 rubli
	 * @param symbol
	 */
	public void wypromuj(int xk, int yk, int symbol) {
		switch(symbol){
			case -1:
				wrazeBierki.add(szachownica[xk][yk]=new Goniec("gfx/goniec.png",BierkaKolor.CZARNY,xk,yk));
				break;
			case -2:
				wrazeBierki.add(szachownica[xk][yk]=new Konik("gfx/kon.png",BierkaKolor.CZARNY,xk,yk));
				break;
			case -3:
				wrazeBierki.add(szachownica[xk][yk]=new Wieza("gfx/wieza.png",BierkaKolor.CZARNY,xk,yk));
				break;
			case -4:
				wrazeBierki.add(szachownica[xk][yk]=new Krolowa("gfx/krolowa.png",BierkaKolor.CZARNY,xk,yk));
				break;
			case 1:
				wrazeBierki.add(szachownica[xk][yk]=new Goniec("gfx/goniecbialy.png",BierkaKolor.BIALY,xk,yk));
				break;
			case 2:
				wrazeBierki.add(szachownica[xk][yk]=new Konik("gfx/konbialy.png",BierkaKolor.BIALY,xk,yk));
				break;
			case 3:
				wrazeBierki.add(szachownica[xk][yk]=new Wieza("gfx/wiezabiala.png",BierkaKolor.BIALY,xk,yk));
				break;
			case 4:
				wrazeBierki.add(szachownica[xk][yk]=new Krolowa("gfx/krolowabiala.png",BierkaKolor.BIALY,xk,yk));
				break;
		}
		chessGuiWindow.repaint();
	}
	
	
	private class OdgrywaczDzwieku implements Runnable{
		/**
		 * odbicie od ściany
		 */
		private Clip scianka;
		/**
		 * metoda odgrywa dźwięk w nowym watku
		 */
		OdgrywaczDzwieku(){
			try {
				
			 //AudioInputStream stream = AudioSystem.getAudioInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("gfx/klocek.wav"));
			 AudioInputStream stream = AudioSystem.getAudioInputStream(new File("gfx/klocek.wav"));
		        AudioFormat format = stream.getFormat();
		        if (format.getEncoding() != AudioFormat.Encoding.PCM_UNSIGNED) {
		            format = new AudioFormat(
		                    AudioFormat.Encoding.PCM_UNSIGNED,
		                    format.getSampleRate(),
		                    format.getSampleSizeInBits()*2,
		                    format.getChannels(),
		                    format.getFrameSize()*1,
		                    format.getFrameRate(),
		                    true);       
		            stream = AudioSystem.getAudioInputStream(format, stream);
		        }
		    
		
		        DataLine.Info info = new DataLine.Info(
		            Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
		        scianka = (Clip) AudioSystem.getLine(info);
		    	scianka.open(stream);
		       
		        
		    } catch (IOException e) {
		    } catch (LineUnavailableException e) {
		    } catch (UnsupportedAudioFileException e) {
		    }
			
		}
		/**
		 * metoda uruchamia wątek
		 */
		public void run(){
			scianka.start();
		}
	}
}
